--------Facemaker Golden Edition-------
A 4am crack                  2017-06-10
---------------------------------------

Name: Facemaker Golden Edition
Genre: educational
Year: 1986
Credits:
  program by James Bach
  artwork by Bill Groetzinger
  Dale Disharoon, Inc.
Publisher: Spinnaker Software
Media: single-sided 5.25-inch floppy
OS: DOS 3.3
Previous cracks: none
Similar cracks:
  #279 The Math Busters

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  no errors, but copy hangs after title
  screen

Locksmith Fast Disk Backup
  ditto

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  nothing unusual

Disk Fixer
  T00-T02 -> looks like DOS 3.3
  T01,S09 -> startup program is
    "FACEMAKER"
  T11 -> DOS-style disk catalog

Why didn't any of my copies work?
  I don't know. Maybe a nibble check
  called from the startup program?

Next steps:

  1. Trace startup program
  2. Find nibble check and disable it
  3. Declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
      In Which We Quickly Descend
      Into A Hellish Nightmare Of
      Encryption And Obfuscation


[S6,D1=original disk]

]PR#6
<Ctrl-C> gets me a working prompt

]CATALOG

DISK VOLUME 254

 A 002 FACEMAKER
 A 003 LOADER
 B 033 TITLE
 B 005 TEXT FONT
 B 010 FONT
 B 055 PROGRAM
 B 005 TABLES

]LIST

 10  HOME
 20  POKE 104,65: POKE 16384 + 25
     6,0
 30  PRINT "RUN LOADER"

]LOAD LOADER
]LIST

 5  HOME
 15  PRINT "BLOAD TITLE,A$2000"
 16  VTAB 22: HTAB 13: PRINT "COP
     YRIGHT 1986": HTAB 9: PRINT
     "SPINNAKER SOFTWARE CORP.": VTAB
     24: HTAB 11: PRINT "ALL RIGH
     TS RESERVED";
 17  POKE  - 16297,0: POKE  - 163
     00,0: POKE  - 16301,0: POKE
      - 16304,0
 20  VTAB 1: PRINT
 30  PRINT "BLOAD TEXT FONT,A$140
     0"
 40  PRINT "BLOAD FONT,A$C00"
 50  PRINT "BLOAD TABLES"
 70  VTAB 1: PRINT
 80  PRINT "BLOAD PROGRAM"
 90  POKE  - 16302,0
 91  IF  PEEK (49420) = 20 THEN 9
     4
 92  IF  PEEK (49413) = 56 THEN 9
     5
 93  IF  PEEK (49669) = 56 THEN 9
     5
 94  POKE 0,0: GOTO 100
 95  PRINT  CHR$ (4);"PR#1"
 97  PRINT  CHR$ (13);
 99  PRINT  CHR$ (4);"PR#0": POKE
     0,255
 100  CALL 24576

A quick test confirms that my non-
working copy gets as far as line 100,
so I'm guessing the problem is in the
assembly language code at $6000 (24576
in decimal).

]100 END
]RUN
]CALL -151

*6000L

]BLOAD BOOTER.OBJ,A$92D0
]CALL -151

*6000L

6000-   4C E7 94    JMP   $94E7

*94E7L

; push an address to the stack
94E7-   A9 94       LDA   #$94
94E9-   48          PHA
94EA-   A9 9F       LDA   #$9F
94EC-   48          PHA

; save some zero page values
94ED-   A5 08       LDA   $08
94EF-   8D 2D 95    STA   $952D
94F2-   A5 09       LDA   $09
94F4-   8D 2E 95    STA   $952E

; probably an address, so ($08) points
; to $9300
94F7-   A9 00       LDA   #$00
94F9-   85 08       STA   $08
94FB-   A9 93       LDA   #$93
94FD-   85 09       STA   $09

; decryption loop
94FF-   A9 96       LDA   #$96
9501-   48          PHA
9502-   A0 00       LDY   #$00
9504-   68          PLA
9505-   51 08       EOR   ($08),Y
9507-   91 08       STA   ($08),Y
9509-   48          PHA
950A-   C8          INY
950B-   D0 F7       BNE   $9504
950D-   E6 09       INC   $09
950F-   A5 09       LDA   $09

; so 2 pages total, $9300 and $9400
9511-   C9 95       CMP   #$95
9513-   D0 EF       BNE   $9504

; more decryption
9515-   68          PLA
9516-   51 08       EOR   ($08),Y
9518-   91 08       STA   ($08),Y
951A-   48          PHA
951B-   C8          INY
951C-   98          TYA
951D-   C9 02       CMP   #$02
951F-   D0 F4       BNE   $9515
9521-   68          PLA

; restore zero page
9522-   AD 2D 95    LDA   $952D
9525-   85 08       STA   $08
9527-   AD 2E 95    LDA   $952E
952A-   85 09       STA   $09

; exit by "returning" to the address
; that we pushed earlier --
; $94/$9F, so execution continues at
; $94A0
952C-   60          RTS

This is self-contained. If I start at
$94ED, I can run the decryption and
exit gracefully (since I never pushed
another address to the stack).

*94ED

Here's the decrypted code at $94A0:

*94A0L

; push another address
94A0-   A9 60       LDA   #$60
94A2-   48          PHA
94A3-   A9 21       LDA   #$21
94A5-   48          PHA

94A6-   20 1A 94    JSR   $941A

*941AL

; get RWTS parameter address
941A-   20 E3 03    JSR   $03E3
941D-   8C 38 94    STY   $9438
9420-   8D 39 94    STA   $9439

; set up some RWTS parameters
9423-   A2 04       LDX   #$04
9425-   18          CLC
9426-   BD 00 94    LDA   $9400,X
9429-   6D 38 94    ADC   $9438
942C-   8D 38 94    STA   $9438
942F-   90 03       BCC   $9434
9431-   EE 39 94    INC   $9439
9434-   BD 05 94    LDA   $9405,X
9437-   8D 00 00    STA   $0000
943A-   CA          DEX
943B-   10 E8       BPL   $9425
943D-   20 E3 03    JSR   $03E3
9440-   20 D9 03    JSR   $03D9
9443-   60          RTS

*9400.9404

9400- 08 02 01 01 00

The loop works backwards, starting at
$9404. Each value is the additional
offset into the RWTS parameter table
for the next value (also stored
backwards, at $9405+). So we're setting
RWTS+0, then +1, +2, +4, and +C.

*9405.9409

9405- 00 00 01 60 01

  RWTS+0 = #$01 (table type, always 1)
  RWTS+1 = #$60 (slot x16, so slot 6)
  RWTS+2 = #$01 (drive, so drive 1)
  RWTS+4 = #$00 (track, so track 0)
  RWTS+C = #$00 (command, seek)

So we're seeking to track 0.

Continuing from $94A9...

; turn on the drive motor manually
; (highly suspicious)
94A9-   AD EA C0    LDA   $C0EA
94AC-   AD E9 C0    LDA   $C0E9

; some parameters (maybe nibbles?)
94AF-   A9 AB       LDA   #$AB
94B1-   8D 0C 94    STA   $940C
94B4-   A9 AF       LDA   #$AF
94B6-   8D 0B 94    STA   $940B
94B9-   20 44 94    JSR   $9444

*9444L

; look for self-sync bytes ($FF nibble)
9444-   AD EE C0    LDA   $C0EE
9447-   AD EC C0    LDA   $C0EC
944A-   10 FB       BPL   $9447
944C-   C9 FF       CMP   #$FF
944E-   D0 F7       BNE   $9447

; look for a specific nibble sequence
; (including the ones set at $94AF)
9450-   AD EC C0    LDA   $C0EC
9453-   10 FB       BPL   $9450
9455-   C9 FF       CMP   #$FF
9457-   D0 EE       BNE   $9447
9459-   AD EC C0    LDA   $C0EC
945C-   10 FB       BPL   $9459
945E-   C9 FF       CMP   #$FF
9460-   F0 F7       BEQ   $9459
9462-   A2 07       LDX   #$07
9464-   10 05       BPL   $946B
9466-   AD EC C0    LDA   $C0EC
9469-   10 FB       BPL   $9466
946B-   DD 0C 94    CMP   $940C,X
946E-   D0 D7       BNE   $9447
9470-   CA          DEX
9471-   D0 F3       BNE   $9466
9473-   A2 02       LDX   #$02
9475-   AD EC C0    LDA   $C0EC
9478-   10 FB       BPL   $9475
947A-   DD 0A 94    CMP   $940A,X
947D-   D0 C8       BNE   $9447
947F-   CA          DEX
9480-   D0 F3       BNE   $9475
9482-   60          RTS

Continuing from $94BC...

; change a value in the array at $9405
; (the track to seek, was previously 0)
94BC-   A9 01       LDA   #$01
94BE-   8D 06 94    STA   $9406

; seek again (now to track 1)
94C1-   20 1A 94    JSR   $941A

; turn on drive motor again (the RWTS
; call would have turned it off after
; seeking)
94C4-   AD E9 C0    LDA   $C0E9

; oh, this is sneaky
94C7-   A9 04       LDA   #$04
94C9-   8D 7E 94    STA   $947E

That looks like initialization of some
sort of counter or something, but it's
really self-modifying code. The address
$947E is part of this loop at the end
of the subroutine at $9444:

9473-   A2 02       LDX   #$02
9475-   AD EC C0    LDA   $C0EC
9478-   10 FB       BPL   $9475
947A-   DD 0A 94    CMP   $940A,X
947D-   D0 C8       BNE   $9447
           ^^
           ++---- this is $947E

947F-   CA          DEX
9480-   D0 F3       BNE   $9475
9482-   60          RTS

That means that, the second time $9444
is called, the penalty for failing the
comparison loop is that you get booted
out to $9483. What's at $9483? I'm
guessing the answer is "bad things."

*9483L

; not gonna return to the caller
9483-   68          PLA
9484-   68          PLA
9485-   68          PLA
9486-   68          PLA

; turn off drive motor manually
9487-   AD E8 C0    LDA   $C0E8
948A-   78          SEI

; wipe memory and hang
948B-   A9 00       LDA   #$00
948D-   AA          TAX
948E-   9D A0 94    STA   $94A0,X
9491-   E8          INX
9492-   D0 FA       BNE   $948E
9494-   9D 8E 93    STA   $938E,X
9497-   CA          DEX
9498-   D0 FA       BNE   $9494
949A-   CE 96 94    DEC   $9496
949D-   4C 94 94    JMP   $9494

...which is exactly the behavior I saw
on my non-working copy.

Continuing from $94CC...

; set up different nibble parameters
94CC-   A9 AB       LDA   #$AB
94CE-   8D 0D 94    STA   $940D
94D1-   A9 AB       LDA   #$AB
94D3-   8D 0C 94    STA   $940C
94D6-   A9 AB       LDA   #$AB
94D8-   8D 0B 94    STA   $940B

; and call nibble check again, but this
; time it won't return unless the disk
; is an original
94DB-   20 44 94    JSR   $9444

; turn off drive motor and exit via the
; address we pushed at $94A0 -- so
; execution continues at $6022
94DE-   AD E8 C0    LDA   $C0E8
94E1-   A0 00       LDY   #$00
94E3-   AD B3 FB    LDA   $FBB3
94E6-   60          RTS

Now I see how this protection check
works. It's a track synchronization
protection. On the first call to $9444,
it searches a nibble sequence including
$AB $AF (set at $94AF). The rest of the
sequence is taken from the 8 nibbles at
$940D (in reverse order) and the two
nibbles at $940B (also in reverse).

*940D.

9408- .. .. .. .. .. AA AA FE
9410- FF 96 AA D5 AD AA D5 EB
9418- AA DE

So the first time (on track 0), we find
$D5 $AA $96 $FF $FE $AA $AA $AB $AF,
which is part of the address prologue
for track 0, sector 7. ($AA $AA as a
4-and-4 encoded value is 0. $AB $AF as
a 4-and-4 encoded value is 7.)

The second time (on track 1), we set
$940B..$940D to $AB $AB $AB, so we end
up looking for
$D5 $AA $96 $FF $FE $AA $AB $AB $AB,
which is part of the address prologue
for track 1, sector 3.

On the original disk, tracks 0 and 1
are physically synchronized so that,
after finding sector 7 on track 0, a
"blind" seek to track 1 will find
sector 3 next. But this depends on the
relative physical layout of the two
tracks on disk; a copy program would
not maintain this relative layout
unless you specifically told it to.
(Bit copiers did have a "synchronize
tracks" option for just such an
occasion.)

If the physical layout of the sectors
on tracks 0 and 1 does not match the
expected pattern, the protection code
knows it's not running on an original
disk.

                   ~

               Chapter 2
        In Which We Emerge From
         The Hellish Nightmare
          And Blink Our Eyes
   At The Simplicity Of The Solution


There are no side effects to this
protection check. (The instructions at
$94E1 that set Y and A are ignored.) I
can bypass it altogether by jumping
directly to $6022 instead of $94E7 --
all the way back at $6000.

Searching the disk for "4C E7 94", I
find this code on track $07.

T07,S0D,$05 E794 -> 2260

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1253
------------------EOF------------------
